
package com.hlkj.pay.app.merchant.ext.impl.lakala.ext;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.constant.FuYouConstant;
import com.hlkj.pay.app.merchant.ext.impl.lakala.constant.LakalaConstant;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.LakalaAppChannelParams;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.LakalaConfig;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.req.LakalaPayReq;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.resp.LakalaPayOrderRes;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.resp.LakalaPayRes;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.resp.LakalaRefundOrderRes;
import com.hlkj.pay.app.merchant.ext.impl.lakala.handler.LakalaMerchantCommonParamsHandle;
import com.hlkj.pay.app.merchant.ext.impl.lakala.utils.LKLSDK;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.order.RefundOrderDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.MerchantUtils;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 拉卡拉 正扫 反扫 通用处理
 * 
 * @author HlpayTeam
 * @date 2024/10/11 16:22
 */
@Slf4j
@Repository
@RequiredArgsConstructor
public class LakalaSceneCommonPayOrder {

    @Autowired
    private  LakalaMerchantCommonParamsHandle lakalaMerchantCommonParamsHandle;

    /**
     * 拉卡拉查询支付订单
     * 
     * @param channelPayOrderQueryContext
     */
    public void commonQueryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        // 配置转换
        LakalaConfig lakalaConfig = lakalaConfig(channelPayOrderQueryContext.getProviderChannelConfigDO(),channelPayOrderQueryContext.isTest());
        PayOrderDO payOrderDO = channelPayOrderQueryContext.getPayOrderDO();
        MerchantAppChannelDO merchantAppChannelDO = channelPayOrderQueryContext.getMerchantAppChannelDO();
        LakalaAppChannelParams lakalaAppChannelParams = lakalaAppChannelParams(merchantAppChannelDO);
        try {
            String openapiDomain = openapiDomain(channelPayOrderQueryContext.isTest());
            LKLSDK.init(lakalaConfig, openapiDomain);
            Map<String, Object> params = new HashMap<>();
            params.put("merchant_no", lakalaAppChannelParams.getMerCupNo());
            params.put("term_no", lakalaAppChannelParams.getTermId());
            params.put("out_trade_no", payOrderDO.getPayOrderNo());
            LakalaPayReq lakalaPayReq = new LakalaPayReq();
            lakalaPayReq.setReq_time(reqTime());
            lakalaPayReq.setReq_data(params);
            String body = JsonUtils.toJsonString(lakalaPayReq);
            log.info("拉卡拉订单查询请求参数:{}", body);
            String response = LKLSDK.httpPost(openapiDomain(channelPayOrderQueryContext.isTest()) + LakalaConstant.MERCHANT_PAY_ORDER_QUERY, body, lakalaConfig.getAppId());
            log.info("拉卡拉订单查询返回参数:{}", JsonUtils.toJsonString(response));
            LakalaPayRes<LakalaPayOrderRes> lakalaRes = JsonUtils.parseObject(response, new TypeReference<LakalaPayRes<LakalaPayOrderRes>>() {
            });
            if (LakalaConstant.SUCCESS_CODE.equals(lakalaRes.getCode())) {
                LakalaPayOrderRes respData = lakalaRes.getResp_data();
                PayOrderDO updartePayOrderDO = new PayOrderDO();
                switch (respData.getTrade_state()) {
                    case "INIT":
                    case "CREATE":
                    case "DEAL":
                    case "UNKNOWN":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                        updartePayOrderDO.setBankMsg("");
                        break;
                    case "PART_REFUND":
                    case "REFUND":
                    case "SUCCESS":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                        updartePayOrderDO.setBankMsg("");
                        break;
                    case "FAIL":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.FAIL.getCode());
                        break;
                    case "CLOSE":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                        break;

                }
                if (StringUtils.hasText(respData.getTrade_time())) {
                    try {
                        updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(respData.getTrade_time()));
                    }
                    catch (Exception e) {
                        log.error("拉卡拉订单查询返回 时间格式化错误 dateTime:{},message:{}", respData.getTrade_time(), e.getMessage());
                    }
                }
                updartePayOrderDO.setInsOrderNo(respData.getAcc_trade_no());
                updartePayOrderDO.setChannelOrderId(respData.getLog_no());
                if (StringUtils.hasText(respData.getAcc_settle_amount())) {
                    updartePayOrderDO.setSettlementAmount(MerchantUtils.fenToYuan(respData.getAcc_settle_amount()));
                }
                if (StringUtils.hasText(respData.getPayer_amount())) {
                    updartePayOrderDO.setPayAmount(MerchantUtils.fenToYuan(respData.getPayer_amount()));
                }
                else {
                    updartePayOrderDO.setPayAmount(payOrderDO.getAmount());
                }
                if (StringUtils.hasText(respData.getUser_id1())) {
                    updartePayOrderDO.setUserId(respData.getUser_id1());
                }
                else {
                    updartePayOrderDO.setUserId(respData.getUser_id2());
                }
                channelPayOrderQueryContext.setUpdartePayOrderDO(updartePayOrderDO);
            }
            else {
                String respDesc = lakalaRes.getMsg();
                channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelPayOrderQueryContext.setRemark(respDesc);
                }
                else {
                    channelPayOrderQueryContext.setRemark("拉卡拉提交支付订单查询返回错误信息为空,错误码为:" + lakalaRes.getMsg());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("拉卡拉提交条码支付请求失败 message:{}", e.getMessage(), e);
            channelPayOrderQueryContext.setRemark("拉卡拉提交条码支付请求失败,失败信息" + e.getMessage());
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            // channelMerchantAccessQueryContext.setAccessStatus(MerchantEnum.CHANNEL_CONFIG_STATUS.APPROVE_REFUSE);
        }
    }

    /**
     * 拉卡拉关闭订单
     * 
     * @param channelPayOrderQueryContext
     */
    public void commonCloseChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        // 配置转换
        LakalaConfig lakalaConfig = lakalaConfig(channelPayOrderQueryContext.getProviderChannelConfigDO(),channelPayOrderQueryContext.isTest());
        PayOrderDO payOrderDO = channelPayOrderQueryContext.getPayOrderDO();
        MerchantAppChannelDO merchantAppChannelDO = channelPayOrderQueryContext.getMerchantAppChannelDO();
        LakalaAppChannelParams lakalaAppChannelParams = lakalaAppChannelParams(merchantAppChannelDO);
        try {
            String openapiDomain = openapiDomain(channelPayOrderQueryContext.isTest());
            LKLSDK.init(lakalaConfig, openapiDomain);
            Map<String, Object> params = new HashMap<>();
            params.put("merchant_no", lakalaAppChannelParams.getMerCupNo());
            params.put("term_no", lakalaAppChannelParams.getTermId());
            params.put("origin_out_trade_no", payOrderDO.getPayOrderNo());
            Map<String, Object> location_info = new HashMap<>();
            location_info.put("request_ip", payOrderDO.getClientIp());
            params.put("location_info", location_info);
            LakalaPayReq lakalaPayReq = new LakalaPayReq();
            lakalaPayReq.setReq_time(reqTime());
            lakalaPayReq.setReq_data(params);
            String body = JsonUtils.toJsonString(lakalaPayReq);
            log.info("拉卡拉关闭订单请求参数:{}", JsonUtils.toJsonString(body));
            String response = LKLSDK.httpPost(openapiDomain(channelPayOrderQueryContext.isTest()) + LakalaConstant.MERCHANT_PAY_ORDER_CLOSE, body, lakalaConfig.getAppId());
            log.info("拉卡拉关闭订单返回参数:{}", JsonUtils.toJsonString(response));
            LakalaPayRes<LakalaPayOrderRes> lakalaRes = JsonUtils.parseObject(response, new TypeReference<LakalaPayRes<LakalaPayOrderRes>>() {
            });
            if (LakalaConstant.SUCCESS_CODE.equals(lakalaRes.getCode())) {
                PayOrderDO updartePayOrderDO = new PayOrderDO();
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                channelPayOrderQueryContext.setUpdartePayOrderDO(updartePayOrderDO);
            }
            else {
                String respDesc = lakalaRes.getMsg();
                channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelPayOrderQueryContext.setRemark(respDesc);
                }
                else {
                    channelPayOrderQueryContext.setRemark("拉卡拉订单关闭返回错误信息为空,错误码为:" + lakalaRes.getCode());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("拉卡拉关闭支付订单请求失败 message:{}", e.getMessage(), e);
            channelPayOrderQueryContext.setRemark("拉卡拉关闭支付订单请求失败,失败信息" + e.getMessage());
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
        }
    }

    /**
     * 拉卡拉查询关闭订单状态
     * 
     * @param channelPayOrderQueryContext
     */
    public void commonQueryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        // 拉卡拉无此接口
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
        channelPayOrderQueryContext.setUpdartePayOrderDO(updartePayOrderDO);
    }

    /**
     * 拉卡拉订单退款
     * 
     * @param channelRefundPayOrderContext
     */
    public void commonRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        // 配置转换
        LakalaConfig lakalaConfig = lakalaConfig(channelRefundPayOrderContext.getProviderChannelConfigDO(),channelRefundPayOrderContext.isTest());
        PayOrderDO payOrderDO = channelRefundPayOrderContext.getPayOrderDO();
        RefundOrderDO refundOrderDO = channelRefundPayOrderContext.getRefundOrderDO();
        MerchantAppChannelDO merchantAppChannelDO = channelRefundPayOrderContext.getMerchantAppChannelDO();
        LakalaAppChannelParams lakalaAppChannelParams = lakalaAppChannelParams(merchantAppChannelDO);
        try {
            String openapiDomain = openapiDomain(channelRefundPayOrderContext.isTest());
            LKLSDK.init(lakalaConfig, openapiDomain);
            Map<String, Object> params = new HashMap<>();
            params.put("merchant_no", lakalaAppChannelParams.getMerCupNo());
            params.put("term_no", lakalaAppChannelParams.getTermId());
            params.put("out_trade_no", refundOrderDO.getRefundOrderNo());
            params.put("origin_out_trade_no", payOrderDO.getPayOrderNo());
            params.put("refund_amount", MerchantUtils.YuanToFen(refundOrderDO.getRefundAmount()));
            params.put("refund_reason", refundOrderDO.getRefundReason());
            Map<String, Object> location_info = new HashMap<>();
            location_info.put("request_ip", payOrderDO.getClientIp());
            params.put("location_info", location_info);
            LakalaPayReq lakalaPayReq = new LakalaPayReq();
            lakalaPayReq.setReq_time(reqTime());
            lakalaPayReq.setReq_data(params);
            String body = JsonUtils.toJsonString(lakalaPayReq);
            log.info("拉卡拉订单退款请求参数:{}", JsonUtils.toJsonString(body));
            String response = LKLSDK.httpPost(openapiDomain(channelRefundPayOrderContext.isTest()) + LakalaConstant.MERCHANT_PAY_ORDER_REFUND, body, lakalaConfig.getAppId());
            log.info("拉卡拉订单退款返回参数:{}", JsonUtils.toJsonString(response));
            LakalaPayRes<LakalaRefundOrderRes> lakalaRes = JsonUtils.parseObject(response, new TypeReference<LakalaPayRes<LakalaRefundOrderRes>>() {
            });
            if (LakalaConstant.SUCCESS_CODE.equals(lakalaRes.getCode())) {
                LakalaRefundOrderRes respData = lakalaRes.getResp_data();
                RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
                if (StringUtils.hasText(respData.getPayer_amount())) {
                    updateRefundOrderDO.setActualRefundAmount(MerchantUtils.fenToYuan(respData.getPayer_amount()));
                }
                updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());

                channelRefundPayOrderContext.setUpdateRefundOrderDO(updateRefundOrderDO);
            }
            else {
                String respDesc = lakalaRes.getMsg();
                channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelRefundPayOrderContext.setRemark(respDesc);
                }
                else {
                    channelRefundPayOrderContext.setRemark("拉卡拉提交退款订单返回错误信息为空,错误码为:" + lakalaRes.getMsg());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("拉卡拉提交退款订单请求失败 message:{}", e.getMessage(), e);
            channelRefundPayOrderContext.setRemark("拉卡拉提交退款订单请求失败,失败信息" + e.getMessage());
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
        }
    }

    /**
     * 拉卡拉订单退款状态查询
     * 
     * @param channelRefundPayOrderContext
     */
    public void commonQueryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        // 配置转换
        LakalaConfig lakalaConfig = lakalaConfig(channelRefundPayOrderContext.getProviderChannelConfigDO(),channelRefundPayOrderContext.isTest());
        RefundOrderDO refundOrderDO = channelRefundPayOrderContext.getRefundOrderDO();
        MerchantAppChannelDO merchantAppChannelDO = channelRefundPayOrderContext.getMerchantAppChannelDO();
        LakalaAppChannelParams lakalaAppChannelParams = lakalaAppChannelParams(merchantAppChannelDO);
        try {
            String openapiDomain = openapiDomain(channelRefundPayOrderContext.isTest());
            LKLSDK.init(lakalaConfig, openapiDomain);
            Map<String, Object> params = new HashMap<>();
            params.put("merchant_no", lakalaAppChannelParams.getMerCupNo());
            params.put("term_no", lakalaAppChannelParams.getTermId());
            params.put("out_trade_no", refundOrderDO.getRefundOrderNo());
            LakalaPayReq lakalaPayReq = new LakalaPayReq();
            lakalaPayReq.setReq_time(reqTime());
            lakalaPayReq.setReq_data(params);
            String body = JsonUtils.toJsonString(lakalaPayReq);
            log.info("拉卡拉退款订单查询请求参数:{}", body);
            String response = LKLSDK.httpPost(openapiDomain(channelRefundPayOrderContext.isTest()) + LakalaConstant.MERCHANT_PAY_ORDER_QUERY, body, lakalaConfig.getAppId());
            log.info("拉卡拉退款订单查询返回参数:{}", JsonUtils.toJsonString(response));
            LakalaPayRes<LakalaPayOrderRes> lakalaRes = JsonUtils.parseObject(response, new TypeReference<LakalaPayRes<LakalaPayOrderRes>>() {
            });
            if (LakalaConstant.SUCCESS_CODE.equals(lakalaRes.getCode())) {
                LakalaPayOrderRes respData = lakalaRes.getResp_data();
                RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
                switch (respData.getTrade_state()) {
                    case "FAIL":
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.FAIL.getCode());
                        updateRefundOrderDO.setBankMsg(respData.getTrade_state_desc());
                        break;
                    case "PART_REFUND":
                        break;
                    case "SUCCESS":
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUND.getCode());
                        if (StringUtils.hasText(respData.getTrade_time())) {
                            try {
                                updateRefundOrderDO.setSuccessTime(DateUtils.shanghaiDate(respData.getTrade_time()));
                            }
                            catch (Exception e) {
                                log.error("拉卡拉退款订单查询返回 时间格式化错误 dateTime:{},message:{}", respData.getTrade_time(), e.getMessage());
                            }
                        }
                        break;
                    default:
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());
                }

                channelRefundPayOrderContext.setUpdateRefundOrderDO(updateRefundOrderDO);
            }
            else {
                String respDesc = lakalaRes.getMsg();
                channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelRefundPayOrderContext.setRemark(respDesc);
                }
                else {
                    channelRefundPayOrderContext.setRemark("拉卡拉退款订单查询返回错误信息为空,错误码为:" + lakalaRes.getMsg());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("拉卡拉退款订单查询请求失败 message:{}", e.getMessage(), e);
            channelRefundPayOrderContext.setRemark("拉卡拉提交条码支付请求失败,失败信息" + e.getMessage());
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            // channelMerchantAccessQueryContext.setAccessStatus(MerchantEnum.CHANNEL_CONFIG_STATUS.APPROVE_REFUSE);
        }
    }

    public String h5Domain(boolean test) {
        return test ? FuYouConstant.PAY_ORDER_H5_DOMAIN_TEST : FuYouConstant.PAY_ORDER_H5_DOMAIN_PROD;
    }

    public Integer expireMinute(Long time) {
        if (time == null) {
            return 30;
        }
        return (int) (time - System.currentTimeMillis()) / 1000 / 60;
    }

    public String date(Long time) {
        if (time == null) {
            time = System.currentTimeMillis();
        }
        LocalDateTime localDateTime = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()).toLocalDateTime();
        DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        return localDateTime.format(sdf);
    }

    public LakalaAppChannelParams lakalaAppChannelParams(MerchantAppChannelDO merchantAppChannelDO) {
        return JsonUtils.parseObject(merchantAppChannelDO.getAppConfigJson(), LakalaAppChannelParams.class);
    }

    public LakalaConfig lakalaConfig(ProviderChannelConfigDO providerChannelConfigDO, boolean test) {
        return lakalaMerchantCommonParamsHandle.lakalaConfig(providerChannelConfigDO, test).getData();
    }

    public String reqTime() {
        return DateUtils.getDateString("yyyyMMddHHmmss");
    }

    public String openapiDomain(boolean test) {
        // 测试环境：https://test.wsmsd.cn/sit/api/v2/mms/openApi/wechatParamConf
        // 生产环境：https://s2.lakala.com/api/v2/mms/openApi/wechatParamConf
        if (test) {
            return "https://test.wsmsd.cn/sit";
        }
        else {
            return "https://s2.lakala.com";
        }
    }
}
